//#########################################################
//#
//# Titre : 	Utilitaires Liste Chainee et CHAT LINUX Automne 13
//#			SIF-1015 - Systeme d'exploitation
//#			Universite du Quebec a Trois-Rivieres
//#
//# Auteur : 	Francois Meunier
//#	Date :	Septembre 2013
//#
//# Langage : 	ANSI C on LINUX 
//#
//#######################################

#include "gestionListeChaineeCHAT.h"

//Pointeur vers l'écran
extern WINDOW* contentscr;

//Pointeur de tete de liste
extern struct noeud* head;

//Pointeur de queue de liste pour ajout rapide
extern struct noeud* queue;

//Semaphore de gestion de la tete de la liste
extern sem_t headSem;

//Semaphore de gestion de la queue de la liste
extern sem_t tailSem;

//Semaphore de gestion du texte
extern sem_t texteSem;

//#######################################
//#
//# Recherche le PREDECESSEUR d'un item dans la liste chainee
//#
//# RETOUR: Le pointeur vers le predecesseur est retourne
//# 		
//#			
//# 		Retourne NULL dans le cas ou l'item est introuvable
//#
struct noeud * findPrevCHAT(const char* ptrNick, const char* ptrGroupe){

	//La liste est vide 
	if ((head==NULL)&&(queue==NULL)) 
		return NULL;

	//Pointeur de navigation
	struct noeud * ptr = head;

	//Tant qu'un item suivant existe
	while (ptr->suivant!=NULL)
	{
		//Est-ce le predecesseur de l'item recherche?
		if(
		(strcmp(ptr->suivant->membre.ptrNick,ptrNick) == 0) 
		&& (strcmp(ptr->suivant->membre.ptrGroupe,ptrGroupe) == 0))
		{
		
			//On retourne un pointeur sur l'item precedent
			return ptr;
		}

		//Deplacement du pointeur de navigation
		ptr=ptr->suivant;
		}

		//On retourne un pointeur NULL
		return NULL;
	}

//#######################################
//#
//# Ajoute un item dans la liste chainee
//#
void* addItemCHAT(void* membre)
{	
	//Transforme le pointer void* en pointeur de ma structure
	struct infoMembre* param = (struct infoMembre*)membre;
	
	//Recuperation des valeur du parametre
	char* ptrNick = param->ptrNick;
	char* ptrGroupe = param->ptrGroupe;
	int idMembre = param->identifiant;
	int pid = param->pid;

	//Creation de l'enregistrement en memoire
	struct noeud* ni = (struct noeud*)malloc(sizeof(struct noeud));

	//Affectation des valeurs des champs
	strcpy(ni->membre.ptrNick, ptrNick);
	strcpy(ni->membre.ptrGroupe, ptrGroupe);
	ni->membre.identifiant = idMembre;
	ni->membre.pid = pid;
	sprintf(ni->membre.fifoName, CLIENT_FIFO_NAME, param->pid);

	//Initialise le semaphore du noeud
	Sem_init(&(ni->sem), 0, 1);

	//Bloque le sémaphores de tete
	P(&headSem);
	
		if(head == NULL) // ajout au debut de la liste vide
		{
			//  premier noeud
			P(&(ni->sem));
				ni->suivant= NULL;
				queue = head = ni;
			V(&(ni->sem));

			//Libere le sémaphores de tete
			V(&headSem);
		}
		else  // ajout a la fin de la liste
		{
			//Libere le sémaphores de tete
			V(&headSem);

			//Bloque le sémaphores de queue
			P(&tailSem);

				P(&(queue->sem));
					struct noeud* tptr = queue;

					P(&(ni->sem));
						ni->suivant= NULL;
						queue = ni;
						tptr->suivant = ni;
					V(&(ni->sem));
				V(&(tptr->sem));

			//Libre le sémaphores de queue
			V(&tailSem);	
		}

	char message[MESSAGE_SIZE];
	memset(message, '\0', sizeof(message));
	sprintf(message,"L'usager #%i - %s a ete ajoute au groupe %s!\n", idMembre, ptrNick, ptrGroupe);
	
	broadcastAllExceptSelf('R', message, ni->membre.pid);

	memset(message, '\0', sizeof(message));
	sprintf(message,"Vous avez ete ajoute au groupe %s!\n", ptrGroupe);
	sendMessage(ni->membre.fifoName, 'R', message);

	//Libere la memoire du parametre
	free(param);

	pthread_exit(0);
}

//#######################################
//#
//# Modifie un item de la liste chainee
//#

void* modifyItemCHAT(void* membre)
{
	//Transforme le pointer void* en pointeur de ma structure
	struct infoMembre* param = (struct infoMembre*)membre;
	
	struct noeud * ptr;
	struct noeud * tempnode;

	//Recuperation des valeur du parametre
	char* ptrNick = param->ptrNick;
	char* ptrGroupe = param->ptrGroupe;
	int identifiant = param->identifiant;

	//initialisation de la fifo client
	char nomFifoClient[FIFO_NAME_SIZE];
	memset(nomFifoClient, '\0', sizeof(nomFifoClient));
	sprintf(nomFifoClient, CLIENT_FIFO_NAME, param->pid);

	//Indique que c'est la premiere fois qu'on passe dans la boucle
	int premier = TRUE;
	//Indicateur de modification de l'usager
	int CanModify = TRUE;

	
	char message[MESSAGE_SIZE];


	//Bloque le sémaphore de tête
	P(&headSem);
	//Bloque le sémaphore de queue
	P(&tailSem);

	if ((identifiant<1) || ((head==NULL) && (queue==NULL)) )
	{
		//Libère le sémaphore de queue
		V(&tailSem);
		//Libère le sémaphore de tête
		V(&headSem);

		memset(message, '\0', sizeof(message));
		sprintf(message,"La liste est vide; impossible de modifier l'usager #%i" , identifiant);
		sendMessage(nomFifoClient, 'F', message);

		free(param);
	
		pthread_exit(0);
	}
		
	//Libère le sémaphore de queue
	V(&tailSem);

	//Recherche de l'element a modifier
	ptr = head;//premier element

	if (ptr!=NULL)
	{
		P(&(ptr->sem));

		while (ptr!=NULL)
		{
			//Element a modifier
			if (identifiant == ptr->membre.identifiant && param->pid == ptr->membre.pid)
			{
				//Affectation des valeurs des champs
				strcpy(ptr->membre.ptrNick, ptrNick);
				strcpy(ptr->membre.ptrGroupe, ptrGroupe);

				//Libère le sémaphore du noeud courant
				V(&(ptr->sem));
			
				if (premier == TRUE)
				{
					premier = FALSE;

					//Libère le sémaphore de tête
					V(&headSem);	
				}

				memset(message, '\0', sizeof(message));
				sprintf(message,"L'usager #%i s'apelle maintenant %s et fait partie du groupe %s!\n", identifiant, ptrNick, ptrGroupe);
	
				broadcastAllExceptSelf('R', message, ptr->membre.pid);

				memset(message, '\0', sizeof(message));
				sprintf(message,"Vos nouvelles informations sont : %s %s!\n", ptrNick, ptrGroupe);
				sendMessage(ptr->membre.fifoName, 'R', message);

				free(param);

				pthread_exit(0);
			}
			else
			{
				//Bloque le sémaphore du noeud suivant le courant s'il n'est pas null
				if(ptr->suivant != NULL)
					P(&(ptr->suivant->sem));

				tempnode = ptr;

				//Transfert le noeud suivant dans le pointeur courant
				ptr = ptr->suivant;

				//Libère le sémaphore du noeud courant			
				V(&tempnode->sem);
			}				

			if (premier == TRUE)
			{
				premier = FALSE;

				//Libère le sémaphore de tête
				V(&headSem);	
			}
		}
	}
	else
	{
		//Libère le sémaphore de tête
		V(&headSem);

		memset(message, '\0', sizeof(message));
		sprintf(message,"Element introuvable; impossible de modifier l'usager #%i\n", identifiant);
		sendMessage(nomFifoClient, 'F', message);

		CanModify = FALSE;

	}	
	

	if(CanModify)
	{
		memset(message, '\0', sizeof(message));
		sprintf(message,"Vous ne pouvez modifier cet usager\n");
		sendMessage(nomFifoClient, 'F', message);
	}

	free(param);

	pthread_exit(0);
}

//#######################################
//#
//# Retire un item de la liste chainee
//#
void* removeItemCHAT(void* membre)
{
	//Transforme le pointer void* en pointeur de ma structure
	struct suppressionMembre* param = (struct suppressionMembre*)membre;
	
	struct noeud * ptr;
	struct noeud * optr;

	//Recuperation des valeur du parametre
	char* ptrNick = param->ptrNick;
	char* ptrGroupe = param->ptrGroupe;
	int hasBeenDeleted = FALSE;
	
	//initialisation de la fifo client
	char nomFifoClient[FIFO_NAME_SIZE];
	memset(nomFifoClient, '\0', sizeof(nomFifoClient));
	sprintf(nomFifoClient, CLIENT_FIFO_NAME, param->pid);

	char message[MESSAGE_SIZE];

	//Bloque les sémaphores de tete et de queue
	P(&headSem);
	P(&tailSem);

		//Verification sommaire liste non vide
		if ((head==NULL) && (queue==NULL))
		{ 	
			//Libere les sémaphores de tete et de queue
			V(&tailSem);
			V(&headSem);

			memset(message, '\0', sizeof(message));
			sprintf(message,"La liste est vide; impossible de supprimer l'usager %s\n", ptrNick);
			sendMessage(nomFifoClient, 'F', message);

			//Libere la memoire du parametre
			free(param);
			
			pthread_exit(0);
		}

		//Pointeur de recherche
		if((strcmp(head->membre.ptrNick,ptrNick) == 0) && (strcmp(head->membre.ptrGroupe,ptrGroupe) == 0) )
		{
			ptr = head; // suppression du premier element de la liste
		}
		else 
		{
			ptr = findPrevCHAT(ptrNick,ptrGroupe);
		}

		//Item trouve
		if (ptr!=NULL && param->pid == ptr->membre.pid)
		{
			// Memorisation du pointeur de l'item en cours de suppression
			// Ajustement des pointeurs
			if(head == ptr) // suppression de l'element de tete
			{
				if(head==queue) // un seul element dans la liste
				{
					Sem_destroy(&(ptr->sem));
					free(ptr);

					queue = head = NULL;
				}
				else
				{
					head = ptr->suivant;

					Sem_destroy(&(ptr->sem));
					free(ptr);
				}
			}
			else if (queue==ptr->suivant) // suppression de l'element de queue
			{
				queue=ptr;
		
				Sem_destroy(&(ptr->suivant->sem));
				free(ptr->suivant);

				ptr->suivant=NULL;
			}
			else // suppression d'un element dans la liste
			{
				optr = ptr->suivant;	
				ptr->suivant = ptr->suivant->suivant;

				Sem_destroy(&(optr->sem));
				free(optr);
			}

			hasBeenDeleted = TRUE;
		}

	//Libere les sémaphores de tete et de queue
	V(&tailSem);
	V(&headSem);

	if(hasBeenDeleted)
	{
		memset(message, '\0', sizeof(message));
		sprintf(message,"L'usager %s a quitter le chat\n", ptrNick);
		broadcastAll('R', message);

		memset(message, '\0', sizeof(message));
		sprintf(message,"Votre usager %s a ete supprimer du groupe %s.\n", ptrNick,ptrGroupe);
		sendMessage(nomFifoClient, 'R', message);
	}
	else
	{
		memset(message, '\0', sizeof(message));
		sprintf(message,"Impossible de supprimer l'usager %s\n", ptrNick);
		sendMessage(nomFifoClient,'F', message);
	}

	//Libere la memoire du parametre
	free(param);

	pthread_exit(0);
}


//#######################################
//#
//# Affiche les items dont le numero sequentiel est compris dans un intervalle
//#
void* listItemsCHAT(void* intervalle)
{
	//Transforme le pointer void* en pointeur de ma structure
	struct paramsIntervalle* param = (struct paramsIntervalle*)intervalle;
	
	int start = param->debut;
	int end = param->fin;
	struct noeud * tempNode;

	//initialisation de la fifo client
	char nomFifoClient[FIFO_NAME_SIZE];
	memset(nomFifoClient, '\0', sizeof(nomFifoClient));
	sprintf(nomFifoClient, CLIENT_FIFO_NAME, param->pid);

	char message[MESSAGE_SIZE];

	//Indique que c'est la premiere fois qu'on passe dans la boucle
	int premier = TRUE;

	//Bloque le sémaphore de texte
	P(&texteSem);

		//Bloque le sémaphore de tete
		P(&headSem);
		struct noeud * ptr = head;//premier element
		int id = 0; 

		if(ptr != NULL)
		{	
			//Affichage des entetes de colonnes
			memset(message, '\0', sizeof(message));
			sprintf(message, "\nID               NICK             GROUPE               \n");
			sendMessage(nomFifoClient, 'R', message);

			memset(message, '\0', sizeof(message));
			sprintf(message, "======================================================\n");
			sendMessage(nomFifoClient, 'R', message);
			
			//Bloque le noeud courant
			P(&(ptr->sem));
			
			while (ptr!=NULL)
			{
				id = ptr->membre.identifiant;//identifiant de l'élément courant

				//L'item a un numero sequentiel dans l'interval defini
				if ((id >= start) && (id <= end))
				{
					//Impression de l'élément courant dont l'identifiant respecte l'interval
					memset(message, '\0', sizeof(message));
					sprintf(message, "%i \t\t %s \t\t  %s\n",
						ptr->membre.identifiant,
						ptr->membre.ptrNick,
						ptr->membre.ptrGroupe);
					sendMessage(nomFifoClient, 'R', message);
				}
				
				//Bloque le noeud suivant s'il n'est pas null
				if(ptr->suivant != NULL)
					P(&(ptr->suivant->sem));

				tempNode = ptr;

				ptr = ptr->suivant;

				//Libere le noeud courant
				V(&(tempNode->sem));

				if (premier == TRUE)
				{
					premier = FALSE;

					//Libère le sémaphore de tête
					V(&headSem);	
				}
			}

			//Affichage des pieds de colonnes
			memset(message, '\0', sizeof(message));
			sprintf(message, "======================================================\n\n");
			sendMessage(nomFifoClient, 'R', message);	
		}
		else
		{
			//Affichage des entetes de colonnes
			memset(message, '\0', sizeof(message));
			sprintf(message, "======================================================\n\n");
			sendMessage(nomFifoClient, 'F', message);

			memset(message, '\0', sizeof(message));
			sprintf(message, "Aucuns elements dans la liste!\n");
			sendMessage(nomFifoClient, 'F', message);

			//Affichage des pieds de colonnes
			memset(message, '\0', sizeof(message));
			sprintf(message, "======================================================\n\n");
			sendMessage(nomFifoClient, 'F', message);

			//Libere le sémaphore de tete
			V(&headSem);
		}
	
	//Libère le sémaphore de texte
	V(&texteSem);	

	//Libere la memoire du parametre
	free(intervalle);

	pthread_exit(0);
}
//#######################################
//#
//# Affiche les items dont le numero sequentiel est compris dans un intervalle
//#
void* quitUser(void* paramQuit)
{
	//Transforme le pointer void* en pointeur de ma structure
	struct paramQuit* param = (struct paramQuit*)paramQuit;
	
	unsigned int userPid = param->pid;


	struct noeud * ptr;
	struct noeud * optr;

	char message[MESSAGE_SIZE];

	//Bloque les sémaphores de tete et de queue
	P(&headSem);
	P(&tailSem);

		//Verification sommaire liste non vide
		if ((head==NULL) && (queue==NULL))
		{ 	
			//Libere les sémaphores de tete et de queue
			V(&tailSem);
			V(&headSem);

			//Libere la memoire du parametre
			free(param);
			
			pthread_exit(0);
		}

		//Pointeur de navigation
		struct noeud * navptr = head;

		char ptrNick[100];
		char ptrGroupe[100];

		//Tant qu'un item suivant existe
		while (navptr !=NULL)
		{
			ptr = NULL;

			if(navptr->membre.pid == userPid)
			{
				if((strcmp(head->membre.ptrNick, navptr->membre.ptrNick) == 0) 
				&& (strcmp(head->membre.ptrGroupe, navptr->membre.ptrGroupe) == 0))
				{
					ptr = head; // suppression du premier element de la liste
				}
				else 
				{
					ptr = findPrevCHAT(navptr->membre.ptrNick, navptr->membre.ptrGroupe);
				}

				strcpy(ptrNick, navptr->membre.ptrNick);
				strcpy(ptrGroupe, navptr->membre.ptrGroupe);
			}

			//Item trouve
			if (ptr!=NULL)
			{
				// Memorisation du pointeur de l'item en cours de suppression
				// Ajustement des pointeurs
				if(head == ptr) // suppression de l'element de tete
				{
					if(head==queue) // un seul element dans la liste
					{
						Sem_destroy(&(ptr->sem));
						free(ptr);

						queue = head = NULL;
					}
					else
					{
						head = ptr->suivant;

						Sem_destroy(&(ptr->sem));
						free(ptr);
					}
				}
				else if (queue==ptr->suivant) // suppression de l'element de queue
				{
					queue=ptr;
	
					Sem_destroy(&(ptr->suivant->sem));
					free(ptr->suivant);

					ptr->suivant=NULL;
				}
				else // suppression d'un element dans la liste
				{
					optr = ptr->suivant;	
					ptr->suivant = ptr->suivant->suivant;

					Sem_destroy(&(optr->sem));
					free(optr);
				}
			}

			//Deplacement du pointeur de navigation
			navptr = navptr->suivant;	
		}

	//Libere les sémaphores de tete et de queue
	V(&tailSem);
	V(&headSem);

	memset(message, '\0', sizeof(message));
	sprintf(message,"Usager %s a quitte le chat!\n", ptrNick);
	broadcastAllExceptSelf('R', message, userPid);


	//Libere la memoire du parametre
	free(param);

	pthread_exit(0);
}
//#######################################
//#
//# Transmission d'un message aux membres d'un groupe 
//#
void* transTextCHAT(void* message)
{
	//Transforme le pointer void* en pointeur de ma structure
	struct paramsTexte* param = (struct paramsTexte*)message;

	//Recuperation des valeur du parametre
	char* ptrNick = param->ptrNick;
	char* ptrGroupe = param->ptrGroupe;
	char* ptrTexte = param->ptrTexte;

	//initialisation de la fifo client
	char nomFifoClient[FIFO_NAME_SIZE];
	memset(nomFifoClient, '\0', sizeof(nomFifoClient));
	sprintf(nomFifoClient, CLIENT_FIFO_NAME, param->pid);

	char fifoMessage[MESSAGE_SIZE];

	memset(fifoMessage, '\0', sizeof(fifoMessage));
	sprintf(fifoMessage, "\n%s dit : %s\n", ptrNick, ptrTexte);
	broadcastAllInGroup('R', fifoMessage, param->pid, ptrGroupe);		

	//Libère la memoire du parametre
	free(message);

	pthread_exit(0);
}
//#######################################
//#
//# Communication avec le client
//#
void sendMessage(char fifo[FIFO_NAME_SIZE], char ack, char message[MESSAGE_SIZE])
{
	int fifoClient = Open_fifo(fifo, O_WRONLY);
		struct Server_Response sr;
		sr.ack = ack;
		strcpy(sr.message, message);
		write(fifoClient, &sr, sizeof(sr));
	close(fifoClient);
}

void broadcastAll(char ack, char message[MESSAGE_SIZE])
{
	broadcastMessage(ack, message, TRUE, 0, FALSE, "");
}

void broadcastAllExceptSelf(char ack, char message[MESSAGE_SIZE], unsigned int selfPid)
{
	broadcastMessage(ack, message, FALSE, selfPid, FALSE, "");
}

void broadcastAllInGroup(char ack, char message[MESSAGE_SIZE], unsigned int selfPid, char groupe[100])
{
	broadcastMessage(ack, message, FALSE, selfPid, TRUE, groupe);
}

void broadcastMessage(char ack, char message[MESSAGE_SIZE], int toSelf, unsigned int selfPid, int toGroup, char groupe[100])
{
	if (head != NULL) 
	{
		//Indique que c'est la premiere fois qu'on passe dans la boucle
		int premier = TRUE;

		//Bloque le sémaphore de tête
		P(&headSem);

		//Pointeur de navigation
		struct noeud * ptr = head;
		struct noeud * tempnode = NULL;

		if (ptr!=NULL)
		{
			P(&(ptr->sem));

			//Tant qu'un item suivant existe
			while (ptr != NULL)
			{
				if(toGroup == TRUE)
				{
					if(strcmp(ptr->membre.ptrGroupe, groupe) == 0 && ptr->membre.pid != selfPid)
						sendMessage(ptr->membre.fifoName, ack, message);
				}
				else if(toSelf == TRUE || (toSelf == FALSE && ptr->membre.pid != selfPid))
				{
					sendMessage(ptr->membre.fifoName, ack, message);
				}

				//Bloque le sémaphore du noeud suivant le courant s'il n'est pas null
				if(ptr->suivant != NULL)
					P(&(ptr->suivant->sem));

				tempnode = ptr;

				//Deplacement du pointeur de navigation
				ptr=ptr->suivant;

				//Libère le sémaphore du noeud courant			
				V(&tempnode->sem);

				if (premier == TRUE)
				{
					premier = FALSE;

					//Libère le sémaphore de tête
					V(&headSem);	
				}
			}
		}
	}
}


